package com.ghp.admin.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ghp.admin.mapper.FileInfoMapper;
import com.ghp.admin.service.FileInfoService;
import com.ghp.common.model.dto.file.FileChunkDTO;
import com.ghp.common.model.entity.FileInfo;
import com.ghp.common.model.enums.FileInfoEnum;
import com.ghp.common.model.vo.FileChunkVO;
import com.ghp.common.utils.response.ResponseResult;
import com.ghp.file.model.FileChunkTag;
import com.ghp.file.model.FileUploadResponse;
import com.ghp.file.request.FileRequest;
import com.ghp.file.utils.FileUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;

import static com.ghp.common.constants.RedisConstants.FILE_CHUNK_KEY;
import static com.ghp.common.constants.RedisConstants.FILE_TAG_KEY;

/**
 * @author ghp
 * @description 针对表【tb_file_info(文件信息表)】的数据库操作Service实现
 * @createDate
 */
@Service
public class FileInfoServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo>
        implements FileInfoService {

    @Resource
    private RedisTemplate redisTemplate;

    @Resource(name = "tencentyun")
    private FileRequest fileRequest;

    /**
     * 上传文件预检
     *
     * @param fileChunkDTO
     * @return
     */
    @Override
    public ResponseResult uploadFilePrecheck(FileChunkDTO fileChunkDTO) {
        String fileId = fileChunkDTO.getIdentifier();
        FileChunkVO fileChunkVO = new FileChunkVO();

        // 判断文件是否已上传完整
        FileInfo fileInfo = this.getOne(new LambdaQueryWrapper<FileInfo>()
                .eq(FileInfo::getIsFinished, FileInfoEnum.UPLOAD_INCOMPLETE.getValue())
                .eq(FileInfo::getIdentifier, fileId));

        if (Objects.nonNull(fileInfo)) {
            // 文件已上传完整
            fileChunkVO.setFileUrl(fileInfo.getFileUrl());
            fileChunkVO.setIsUploaded(FileInfoEnum.UPLOAD_INCOMPLETE.getValue());
            return ResponseResult.success(fileChunkVO);
        }

        // 文件未上传完整，将所有已上传的分片返回给前端
        List<Integer> chunkNumberList = redisTemplate.opsForList().range(FILE_CHUNK_KEY + fileId, 0, -1);
        if (Objects.isNull(chunkNumberList) || chunkNumberList.isEmpty()) {
            fileChunkVO.setIsUploaded(FileInfoEnum.UPLOAD_COMPLETE.getValue());
            return ResponseResult.success(fileChunkVO);
        }
        fileChunkVO.setUploadedChunks(chunkNumberList);
        return ResponseResult.success(fileChunkVO);
    }

    /**
     * 上传文件
     *
     * @param fileChunkDTO
     * @return
     */
    @Transactional
    @Override
    public ResponseResult<String> uploadFile(FileChunkDTO fileChunkDTO) {
        if (fileChunkDTO.getTotalChunks() == 1) {
            // 非分片上传
            return ResponseResult.success(uploadByWhole(fileChunkDTO));
        }
        // 分片上传
        return ResponseResult.success(uploadByChunk(fileChunkDTO));
    }

    /**
     * 单文件整体上传
     */
    private String uploadByWhole(FileChunkDTO fileChunkDTO) {
        FileUploadResponse uploadResponse = fileRequest.upload(fileChunkDTO.getFile());
        String fileUrl = uploadResponse.getFileUrl();
        String filePath = uploadResponse.getFilePath();
        saveFileInfo(fileChunkDTO, fileUrl, filePath);
        return fileUrl;
    }

    /**
     * 分片上传
     */
    private String uploadByChunk(FileChunkDTO fileChunkDTO) {
        // 文件id
        String fileId = fileChunkDTO.getIdentifier();
        String fileChunkKey = FILE_TAG_KEY + fileId;
        String fileTagKey = FILE_CHUNK_KEY+fileId;
        // 文件的可访问URL
        String fileUrl = null;
        // 是否已合并，如果分片合并成功了，则保存文件信息
        boolean isMerged = false;

        // 分片上传
        FileChunkTag fileChunkTag = fileRequest.uploadByChunk(fileId, fileChunkDTO.getFile(),
                fileChunkDTO.getChunkNumber(), fileChunkDTO.getChunkSize(), fileChunkDTO.getTotalChunks());

        // 分片上传成功，则将分片响应信息保存起来（分片合并需要用到）
        redisTemplate.opsForList().leftPush(fileTagKey, fileChunkTag);
        // 分片上传成功，需要保留分片信息，用于实现断点续传功能
        redisTemplate.opsForList().leftPush(fileChunkKey, fileChunkDTO.getChunkNumber());

        // 判断当前分片是否是最后一片，如果是最后一片就需要合并分片
        if (fileChunkDTO.getChunkNumber().equals(fileChunkDTO.getTotalChunks())) {
            // 合并分片
            List<FileChunkTag> fileChunkTagList = redisTemplate.opsForList().range(fileChunkKey, 0, -1);
            fileUrl = fileRequest.mergeChunk(fileChunkTagList);
            // 删除存储的分片信息
            redisTemplate.delete(fileChunkKey);
            redisTemplate.delete(fileTagKey);
            isMerged = true;
        }

        // 判断文件是否上传成功
        if (isMerged) {
            // 分片合并成功后，将文件信息存储到数据库
            saveFileInfo(fileChunkDTO, fileUrl, fileUrl);
        }
        return fileUrl;
    }

    /**
     * 保存文件信息
     */
    private void saveFileInfo(FileChunkDTO fileChunkDTO, String fileUrl, String filePath) {
        FileInfo fileInfo = new FileInfo();
        BeanUtils.copyProperties(fileChunkDTO, fileInfo);
        fileInfo.setRealName(fileChunkDTO.getFilename());
        fileInfo.setFileUrl(fileUrl);
        fileInfo.setFilePath(filePath);
        fileInfo.setFileSuffix(FileUtils.getFileExtension(fileChunkDTO.getFilename()));
        fileInfo.setFileSize(fileChunkDTO.getTotalSize());
        fileInfo.setIsFinished(FileInfoEnum.UPLOAD_INCOMPLETE.getValue());
        this.save(fileInfo);
    }
}




